﻿/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://github.com/shenyczz/CSharpKit
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/

using System;
using CSharpKit.Numerics.Distributions;
using CSharpKit.Numerics.LinearAlgebra.Solvers;

namespace CSharpKit.Numerics.LinearAlgebra
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class MatrixBuilder<T> : Numeric<T>
        where T : struct, IFormattable, IEquatable<T>
    {

        #region OfStorage

        /// <summary>
        /// MatrixStorageDense <br/>
        /// MatrixStorageSparse <br/>
        /// MatrixStorageDiagonal <br/>
        /// </summary>
        /// <param name="storage">存储类</param>
        /// <returns></returns>
        public Matrix<T> OfStorage(MatrixStorage<T> storage)
        {
            if (storage == null)
                throw new ArgumentNullException(nameof(storage));

            if (storage is MatrixStorageDense<T> dense)
                return Dense(dense);

            if (storage is MatrixStorageSparse<T> sparse)
                return Sparse(sparse);

            if (storage is MatrixStorageDiagonal<T> diagonal)
                return Diagonal(diagonal);

            throw new NotSupportedException(string.Format("Matrix storage type '{0}' is not supported. Only DenseColumnMajorMatrixStorage, SparseCompressedRowMatrixStorage and DiagonalMatrixStorage are supported as this point.", storage.GetType().Name));
        }

        /// <summary>
        /// 在具体类型的派生类中重载
        /// </summary>
        public abstract Matrix<T> Dense(IMatrixStorage storage);
        public abstract Matrix<T> Sparse(IMatrixStorage storage);
        public abstract Matrix<T> Diagonal(IMatrixStorage storage);

        #endregion


        #region Dense

        public Matrix<T> Dense(int rows, int columns)
        {
            var storage = new MatrixStorageDense<T>(rows, columns);
            return OfStorage(storage);
        }
        public Matrix<T> Dense(int rows, int columns, T tvalue)
        {
            var storage = tvalue.Equals(Zero)
                ? new MatrixStorageDense<T>(rows, columns)
                : MatrixStorage<T>.OfValue(rows, columns, tvalue);
            return OfStorage(storage);
        }
        public Matrix<T> Dense(int rows, int columns, T[] data)
        {
            var storage = new MatrixStorageDense<T>(rows, columns, data);
            return OfStorage(storage);
        }
        public Matrix<T> Dense(int rows, int columns, Func<int, int, T> init)
        {
            return Dense(MatrixStorageDense<T>.OfInit(rows, columns, init));
        }

        public Matrix<T> DenseOfArray(T[,] array)
        {
            return Dense(MatrixStorageDense<T>.OfArray(array));
        }

        /// <summary>
        /// 创建一个新的对角线密集恒等矩阵（对角线元素为 1）=> 密集单位阵
        /// </summary>
        public Matrix<T> DenseIdentity(int order)
        {
            return Dense(MatrixStorageDense<T>.OfDiagonalInit(order, order, i => Numeric<T>.One));
        }

        #endregion

        #region Diagonal

        public Matrix<T> Diagonal(int rows, int columns)
        {
            var storage = new MatrixStorageDiagonal<T>(rows, columns);
            return OfStorage(storage);
        }

        #endregion

        #region Sparse

        public Matrix<T> Sparse(int rows, int columns)
        {
            var storage = new MatrixStorageSparse<T>(rows, columns);
            return OfStorage(storage);
        }


        #endregion


        #region Random - 密集矩阵（随机采样）

        /// <summary>
        /// 创建一个新的密集矩阵，其值用系统随机源从标准分布中采样
        /// </summary>
        /// <param name="rows"></param>
        /// <param name="columns"></param>
        /// <returns></returns>
        public Matrix<T> Random(int rows, int columns)
        {
            return Random(rows, columns, new Normal(SystemRandomSource.Default));
        }
        public Matrix<T> Random(int rows, int columns, int seed)
        {
            return Random(rows, columns, new Normal(new SystemRandomSource(seed, true)));
        }


        //public Matrix<T> RandomPositiveDefinite(int order, IContinuousDistribution distribution)
        //{
        //    var a = Random(order, order, distribution);
        //    return a.ConjugateTransposeThisAndMultiply(a);
        //}


        // 派生类重载该方法
        public abstract Matrix<T> Random(int rows, int columns, IContinuousDistribution distribution);

        #endregion



        #region SameAs

        public Matrix<T> SameAs<TU>(Matrix<TU> example)
            where TU : struct, IEquatable<TU>, IFormattable
        {
            return SameAs(example, example.RowCount, example.ColumnCount);
        }

        public Matrix<T> SameAs(Matrix<T> example, Matrix<T> otherExample)
        {
            return SameAs(example, otherExample, example.RowCount, example.ColumnCount);
        }

        public Matrix<T> SameAs<TU>(Matrix<TU> example, int rows, int columns, bool fullyMutable = false)
            where TU : struct, IEquatable<TU>, IFormattable
        {
            var storage = example.Storage;

            if (storage is MatrixStorageDense<T>)
            {
                return Dense(rows, columns);
            }

            if (storage is MatrixStorageDiagonal<T>)
            {
                return fullyMutable ? Sparse(rows, columns) : Diagonal(rows, columns);
            }

            if (storage is MatrixStorageSparse<T>)
            {
                return Sparse(rows, columns);
            }

            return Dense(rows, columns);
        }

        public Matrix<T> SameAs(Matrix<T> example, Matrix<T> otherExample, int rows, int columns, bool fullyMutable = false)
        {
            var storage1 = example.Storage;
            var storage2 = otherExample.Storage;

            if (storage1 is MatrixStorageDense<T> || storage2 is MatrixStorageDense<T>)
                return Dense(rows, columns);

            if (storage1 is MatrixStorageDiagonal<T> && storage2 is MatrixStorageDiagonal<T>)
                return fullyMutable ? Sparse(rows, columns) : Diagonal(rows, columns);

            if (storage1 is MatrixStorageSparse<T> || storage2 is MatrixStorageSparse<T>)
                return Sparse(rows, columns);

            return Dense(rows, columns);
        }

        public Matrix<T> SameAs(Vector<T> example, int rows, int columns)
        {
            return example.Storage.IsDense ? Dense(rows, columns) : Sparse(rows, columns);
        }


        #endregion


        // 迭代停止规则
        public abstract IIterationStopCriterion<T>[] IterativeSolverStopCriteria(int maxIterations = 1000);

        //}}@@@
    }

}

